// 版权归Go作者所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现rat到字符串的转换功能。

package big

import (
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

func ratTok(ch rune) bool {
	return strings.ContainsRune("+-/0123456789.eE", ch)
}

var ratZero Rat
var _ fmt.Scanner = &ratZero // /*Rat必须实施fmt。扫描仪

// 扫描是fmt的一个支持例行程序。扫描仪。它接受
// /'e'，e'，f'，f'，g'，g'，和'v'格式。所有格式都是等效的。
func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
	tok, err := s.Token(true, ratTok)
	if err != nil {
		return err
	}
	if !strings.ContainsRune("efgEFGv", ch) {
		return errors.New("Rat.Scan: invalid verb")
	}
	if _, ok := z.SetString(string(tok)); !ok {
		return errors.New("Rat.Scan: invalid syntax")
	}
	return nil
}

// SetString将z设置为s的值，并返回z和一个表示
// success的布尔值。s可以表示为（可能是有符号的）分数“a/b”，也可以表示为
// 浮点数，可选后跟一个指数。
// 如果提供了一个分数，则被除数和除数都可以是一个
// 十进制整数，或者单独使用前缀“0b”、“0”或“0o”、
// 或“0x”（或其大写变体）分别表示二进制、八进制或
// 十六进制整数。除数不能被签名。
// 如果提供了浮点数，它可以是十进制形式，也可以是
// 使用与上面相同的任何前缀，但“0”表示非十进制
// 尾数。前导“0”被认为是小数点后的0；在这种情况下，它不表示八进制表示。
// 也可以提供可选的base-10`e'或base-2`p'（或其大写变体）
// 指数，但十六进制浮点数除外，该浮点数
// 只接受（可选的）`p'指数（因为`e'或`e`不能与尾数数字区分开来）。如果指数的绝对值
// 太大，操作可能会失败。
// 整个字符串，而不仅仅是前缀，必须有效才能成功。如果
// 操作失败，则z的值未定义，但返回的值为零。
func (z *Rat) SetString(s string) (*Rat, bool) {
	if len(s) == 0 {
		return nil, false
	}
	// len（s）>0 

	// 解析分数a/b，如果有任何
	if sep := strings.Index(s, "/"); sep >= 0 {
		if _, ok := z.a.SetString(s[:sep], 0); !ok {
			return nil, false
		}
		r := strings.NewReader(s[sep+1:])
		var err error
		if z.b.abs, _, _, err = z.b.abs.scan(r, 0, false); err != nil {
			return nil, false
		}
		// 整个字符串必须已被使用
		if _, err = r.ReadByte(); err != io.EOF {
			return nil, false
		}
		if len(z.b.abs) == 0 {
			return nil, false
		}
		return z.norm(), true
	}

	// 解析浮点数
	r := strings.NewReader(s)

	// 符号
	neg, err := scanSign(r)
	if err != nil {
		return nil, false
	}

	// 尾数
	var base int
	var fcount int // 小数位数计数；如果<=0 
	z.a.abs, base, fcount, err = z.a.abs.scan(r, 0, true)
	if err != nil {
		return nil, false
	}

	// 指数
	var exp int64
	var ebase int
	exp, ebase, err = scanExponent(r, true, true)
	if err != nil {
		return nil, false
	}

	// 应该没有未读字符剩余
	if _, err = r.ReadByte(); err != io.EOF {
		return nil, false
	}

	// 特例0（另请参见第16176期）
	if len(z.a.abs) == 0 {
		return z, true
	}
	// len（z.a.abs）>0 

	// 尾数可能有一个基点（fcount<=0），并且
	// 可能是一个非零指数exp。基点相当于一个
	// base**fcount。指数意味着乘以ebase**exp.
	// 乘法是可交换的，因此我们可以将它们应用于任何
	// 顺序。我们只有2和10的幂，我们把10的幂分解成2和5的幂的乘积。此
	// 可能会减少创建最终分数所需的移位/乘法因子或
	// 除数的大小，具体取决于实际的浮点值。

	// 确定小数点的二进制或十进制指数贡献
	var exp2, exp5 int64
	if fcount < 0 {
		// 尾数有一个小数点ddd。dddd；
		// /-fcount是“.”右边的位数。相应调整相关指数。
		d := int64(fcount)
		switch base {
		case 10:
			exp5 = d
			fallthrough // 10**e==5**e*2**e 
		case 2:
			exp2 = d
		case 8:
			exp2 = d * 3 // 八进制数为3位，每个
		case 16:
			exp2 = d * 4 // 十六进制数为4位，每个
		default:
			panic("unexpected mantissa base")
		}
		// F计数已消耗-不再需要
	}

	// 考虑实际指数
	switch ebase {
	case 10:
		exp5 += exp
		fallthrough // 参见上述故障排除
	case 2:
		exp2 += exp
	default:
		panic("unexpected exponent base")
	}
	// exp已消耗-不再需要

	// 应用exp5贡献
	// （以exp5开头，因此要乘以的数字较小）
	if exp5 != 0 {
		n := exp5
		if n < 0 {
			n = -n
			if n < 0 {
				// /如果-n溢出，可能会发生这种情况-（-1<<63）将变成
				// /-1<<63，这仍然是负数。
				return nil, false
			}
		}
		if n > 1e6 {
			return nil, false // 避免过大的指数
		}
		pow5 := z.b.abs.expNN(natFive, nat(nil).setWord(Word(n)), nil) // 使用z.b.abs 
		if exp5 > 0 {
			z.a.abs = z.a.abs.mul(z.a.abs, pow5)
			z.b.abs = z.b.abs.setWord(1)
		} else {
			z.b.abs = pow5
		}
	} else {
		z.b.abs = z.b.abs.setWord(1)
	}

	// 应用exp2贡献
	if exp2 < -1e7 || exp2 > 1e7 {
		return nil, false // 避免过大的指数
	}
	if exp2 > 0 {
		z.a.abs = z.a.abs.shl(z.a.abs, uint(exp2))
	} else if exp2 < 0 {
		z.b.abs = z.b.abs.shl(z.b.abs, uint(-exp2))
	}

	z.a.neg = neg && len(z.a.abs) > 0 // 0没有符号

	return z.norm(), true
}

// 扫描代表基数10 
// /（`e'、`e'）或基数2的r的最长前缀（`p'，`p'）指数，如果有的话。它返回
// 指数、指数基（10或2）或读取或语法错误（如果有）。
// 
// 如果设置了sepOk，则在连续的
// 指数位数之间可能会出现一个下划线字符``.`；这样的下划线不会改变指数的值。
// 如果没有
// 其他错误，则下划线的错误位置将被报告为错误。如果未设置sepOk，则无法识别下划线，因此
// 将终止扫描，就像任何其他非有效数字的字符一样。
// 
// 指数=（“e”|“e”|“p”|“p”）[符号]数字。
// sign=“+”|“-”。
// digits=位{['.']位}。
// digit=“0”。。。"9" . 
// 
// 只有在设置了base2ok的情况下，才允许使用基2指数。
func scanExponent(r io.ByteScanner, base2ok, sepOk bool) (exp int64, base int, err error) {
	// 一个字符前瞻
	ch, err := r.ReadByte()
	if err != nil {
		if err == io.EOF {
			err = nil
		}
		return 0, 10, err
	}

	// 指数字符
	switch ch {
	case 'e', 'E':
		base = 10
	case 'p', 'P':
		if base2ok {
			base = 2
			break // 确定
		}
		fallthrough // 二进制指数不允许
	default:
		r.UnreadByte() // ch不再属于指数
		return 0, 10, nil
	}

	// 符号
	var digits []byte
	ch, err = r.ReadByte()
	if err == nil && (ch == '+' || ch == '-') {
		if ch == '-' {
			digits = append(digits, '-')
		}
		ch, err = r.ReadByte()
	}

	// prev编码之前看到的字符：它是一个
	// 的“、”0”（一个数字）或“.”（还有别的）。
	// 有效分隔符“”只能出现在数字之后。
	prev := '.'
	invalSep := false

	// 指数值
	hasDigits := false
	for err == nil {
		if '0' <= ch && ch <= '9' {
			digits = append(digits, ch)
			prev = '0'
			hasDigits = true
		} else if ch == '_' && sepOk {
			if prev != '0' {
				invalSep = true
			}
			prev = '_'
		} else {
			r.UnreadByte() // ch不再属于数字
			break
		}
		ch, err = r.ReadByte()
	}

	if err == io.EOF {
		err = nil
	}
	if err == nil && !hasDigits {
		err = errNoDigits
	}
	if err == nil {
		exp, err = strconv.ParseInt(string(digits), 10, 64)
	}
	// 其他错误优先于无效分隔符
	if err == nil && (invalSep || prev == '_') {
		err = errInvalSep
	}

	return
}

// 字符串以“a/b”的形式返回x的字符串表示形式（即使b==1）。
func (x *Rat) String() string {
	return string(x.marshal())
}

// marshal实现返回字节片的字符串
func (x *Rat) marshal() []byte {
	var buf []byte
	buf = x.a.Append(buf, 10)
	buf = append(buf, '/')
	if len(x.b.abs) != 0 {
		buf = x.b.Append(buf, 10)
	} else {
		buf = append(buf, '1')
	}
	return buf
}

// 如果b！=1，
// 如果b==1，则以“a”的形式。
func (x *Rat) RatString() string {
	if x.IsInt() {
		return x.a.String()
	}
	return x.String()
}

// FloatString返回十进制形式的x的字符串表示形式，其精度在小数点后的prec 
// 位数。最后一位四舍五入为
// 最接近的数字，两半从零开始四舍五入。
func (x *Rat) FloatString(prec int) string {
	var buf []byte

	if x.IsInt() {
		buf = x.a.Append(buf, 10)
		if prec > 0 {
			buf = append(buf, '.')
			for i := prec; i > 0; i-- {
				buf = append(buf, '0')
			}
		}
		return string(buf)
	}
	// x.b.abs！=0 

	q, r := nat(nil).div(nat(nil), x.a.abs, x.b.abs)

	p := natOne
	if prec > 0 {
		p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil)
	}

	r = r.mul(r, p)
	r, r2 := r.div(nat(nil), r, x.b.abs)

	// 看看我们是否需要将
	r2 = r2.add(r2, r2)
	if x.b.abs.cmp(r2) <= 0 {
		r = r.add(r, natOne)
		if r.cmp(p) >= 0 {
			q = nat(nil).add(q, natOne)
			r = nat(nil).sub(r, p)
		}
	}

	if x.a.neg {
		buf = append(buf, '-')
	}
	buf = append(buf, q.utoa(10)...) // 如果q==0 

	if prec > 0 {
		buf = append(buf, '.')
		rs := r.utoa(10)
		for i := prec - len(rs); i > 0; i-- {
			buf = append(buf, '0')
		}
		buf = append(buf, rs...)
	}

	return string(buf)
}
